import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { CaslAbilityService } from '../auth/casl-ability.service'
import {
  CaslHandlerType,
  CHECK_POLICIES_KEY,
  PolicyHandlerCallback
} from '../decorators/casl.decorator'
import { requertWithUser } from './role.guard'

@Injectable()
export class CaslGuard implements CanActivate {
  constructor(
    private reflector: Reflector,
    private caslAbilityService: CaslAbilityService
  ) {}
  async canActivate(context: ExecutionContext): Promise<boolean> {
    /* Reflector 用于获取存储在类或方法上的元数据。它是通过 @nestjs/core 提供的
    getAllAndMerge(指定元数据的键,[要读取元数据的目标对象数组])和getAllAndOverride
    getAllAndMerge: 用于读取多个源（方法和类）上的元数据并将它们合并，通常合并成一个数组
    getAllAndOverride：用于类和方法）中获取元数据，并用后来的源覆盖前面的源,适用于覆盖类级别的权限数据 */
    // 1.获取类和方法上元数据的角色信息
    const handlers = this.reflector.getAllAndMerge<PolicyHandlerCallback[]>(
      CHECK_POLICIES_KEY.HANDLER,
      [context.getHandler(), context.getClass()]
    ) //从方法和类上获取策略处理器(策略装饰器) =>CheckPolicies装饰器 =>boolean
    const canHandlers = this.reflector.getAllAndMerge<any[]>(CHECK_POLICIES_KEY.CAN, [
      context.getHandler(),
      context.getClass()
    ]) as CaslHandlerType //从类和方法上获取权限信息(@Can())=> @can()装饰器 => CheckPolicies
    const cannotHandlers = this.reflector.getAllAndMerge<any[]>(CHECK_POLICIES_KEY.CANNOT, [
      context.getHandler(),
      context.getClass()
    ]) as CaslHandlerType ////从类和方法上获取权限信息(@Cannot()) => @cannot装饰器 => CheckPolicies
    //如果用户未设置上述的任何一个，那么直接返回true
    if (!handlers || !canHandlers || !cannotHandlers) {
      return true
    }
    //如果设置了以上三个，需要这三个都是允许都是返回true，才会返回true，否则返回false
    const req = context.switchToHttp().getRequest<requertWithUser>()

    if (req.user?.username) {
      let flag = true //断点测试这里，然后逐过程测试，观察flag的变化
      const ability = await this.caslAbilityService.forRoot(req.user?.username)
      if (handlers) {
        // every()数组方法，测试数组中所有元素都满足指定条件，都满足返回true
        //对于handlers数组中的每个hander函数，传ability作为参数，并检查所有的hander(ability)是否都返回true
        flag = flag && handlers.every((handler) => handler(ability))
      }
      if (flag && canHandlers) {
        //只要上面flag为false，下面就不用执行了，省性能
        if (canHandlers instanceof Array) {
          flag = flag && canHandlers.every((hander) => hander(ability))
        } else if (typeof canHandlers === 'function') {
          // 这里不是数组的就直接传入参数
          flag = flag && canHandlers(ability)
        }
      }
      if (flag && cannotHandlers) {
        if (cannotHandlers instanceof Array) {
          flag = flag && cannotHandlers.every((hander) => hander(ability))
        } else if (typeof cannotHandlers === 'function') {
          flag = flag && cannotHandlers(ability)
        }
      }
      return flag
    } else {
      return false
    }
  }
}
